/*****************************************************************************/
/*                              SOURCE FILE                                  */
/*****************************************************************************/
/*
          $Archive:   $

         $Revision:   $

      Last Checkin:
             $Date:   $
                By:
           $Author:   $

 Last Modification:
          $ModTime:   $

       Description:   This file is part of a project downloaded from the web.
                      It has been reformatted and significantly cleaned up so
                      may not resemble the original file very much at all. The
                      original authors comments follow:

                      VMCrc32.cpp : implementation file for the VMCrc32 class
                                   written by PJ Arends
                                   pja@telus.net

                      based on the CRC-32 code found at
                      http://www.createwindow.com/programming/crc32/crcfile.htm

                      For updates check http://www3.telus.net/pja/crc32.htm

                      This code is provided as is, with no warranty as to it's 
                      suitability or usefulness in any application in which it 
                      may be used. This code has not been tested for UNICODE
                      builds, nor has it been tested on a network (with UNC paths).

                      This code may be used in any way you desire. This file may be
                      redistributed by any means as long as it is not sold for 
                      profit, and providing that this notice and the authors name
                      are included.

                      If any bugs are found and fixed, a note to the author explaining
                      the problem and fix would be nice.

                      created : October 2001
      
                      Revision History:

                      October 11, 2001   - changed SendMessage to PostMessage 
                                           in CRC32ThreadProc
*/
static char OBJECT_ID[] = "$Revision:   $ : $JustDate:   $";
/*****************************************************************************/


#include "StdAfx.h"
#include "VMCRC32.h"
#include <io.h>
#include <fcntl.h>
#include <share.h>
#include <tchar.h>
#include <commctrl.h>
#include <stdio.h>

// use a 100 KB buffer (a larger buffer does not seem to run significantly faster,
// but takes more RAM)
//
#define BUFFERSIZE 102400


/*****************************************************************************/
/*

     FUNCTION NAME:  VMCrc32::CRC32ThreadProc

       DESCRIPTION:  If this function is started as a thread, it will send 
                     PBM_* messages to the supplied progress bar control. When
                     the thread is finished, this function will send a  message
                     of type "WM_CRC_THREAD_DONE" to the parent window of the
                     progress bar. the WPARAM parameter will contain the HANDLE
                     of the thread, and the LPARAM will contain the CRC-32 value
                     of the supplied buffer or file.

             INPUT:  lpVoid -  A pointer to a VM_CRC_THREAD_STRUCT structure, type cast 
                               as a void pointer
            OUTPUT:  none

           RETURNS:  The CRC-32 value of the supplied buffer or file
*/
DWORD WINAPI VMCrc32::CRC32ThreadProc( LPVOID lpVoid )
{
  P_VM_CRC_THREAD_STRUCT pxCtrl    = (P_VM_CRC_THREAD_STRUCT)lpVoid;
  ULONG               ulCRC        = 0xFFFFFFFF;
  HWND                hWndProgress = NULL;

  if ( ::IsWindow( pxCtrl->m_hWndProgress ) )
  {
    // setup the progress bar
    //
    hWndProgress = pxCtrl->m_hWndProgress;
    ::PostMessage( hWndProgress, PBM_SETPOS,     0,   0 );
    ::PostMessage( hWndProgress, PBM_SETRANGE32, 0, 100 );
  }
  if ( pxCtrl->m_pbData )
  {
    // calculate CRC for a buffer
    //
    for ( UINT iOffset = 0; iOffset < pxCtrl->m_iSize; iOffset += BUFFERSIZE )
    {
      pxCtrl->m_poCRC32->Calculate( pxCtrl->m_pbData + iOffset,
                                    ( pxCtrl->m_iSize - iOffset > BUFFERSIZE ) 
                                        ? BUFFERSIZE 
                                        : ( pxCtrl->m_iSize - iOffset ), 
                                    ulCRC );
      if ( ::IsWindow( hWndProgress ) )
      {
        int iPercent = iOffset > pxCtrl->m_iSize 
                      ? 100 : (int)( ( (double) iOffset / (double)pxCtrl->m_iSize ) * 100 );
        ::PostMessage( hWndProgress, PBM_SETPOS, iPercent, 0 );
      }
    }
  }
  else 
  if ( pxCtrl->m_achFileName )
  {
    // calculate CRC for a file
    //
    LONGLONG lDone = 0;
    UINT     uiSize = BUFFERSIZE;
    BYTE     bBuffer[ BUFFERSIZE ];

    // Open the file
    //
    int iFile = _tsopen( pxCtrl->m_achFileName, 
                         _O_RDONLY | _O_SEQUENTIAL | _O_BINARY, 
                         _SH_DENYWR );
    if ( iFile != -1 )
    {
      // Get the file size
      //
      _lseeki64( iFile, 0L, SEEK_SET );
      LONGLONG lLength = _lseeki64( iFile, 0L, SEEK_END );
      _lseeki64( iFile, 0L, SEEK_SET );
        
      // process the file
      //
      while ( uiSize == BUFFERSIZE )
      {
        uiSize = _read( iFile, bBuffer, BUFFERSIZE );
        if ( uiSize )
        {
          pxCtrl->m_poCRC32->Calculate( bBuffer, uiSize, ulCRC );
          if ( ::IsWindow( hWndProgress ) )
          {
            // update the progress bar
            //
            lDone += uiSize;
            int iPercent = (int)( ( (long double)lDone / (long double)lLength ) * 100 );
            ::PostMessage( hWndProgress, PBM_SETPOS, iPercent, 0 );
          }
        }
      }
      _close( iFile );
    }
  }

  ulCRC ^= 0xFFFFFFFF;
    
  if ( IsWindow( hWndProgress ) )
  {
    // notify dialog that we are done
    //
    ::PostMessage( ::GetParent( hWndProgress ), VM_WM_CRC_THREAD_DONE, (WPARAM)pxCtrl->m_hThread, ulCRC );
  }
    
  // clean up
  // Known Problem : If the thread is prematurely terminated,
  // this cleanup code is never run, resulting in a memory leak.
  //
  delete pxCtrl->m_pbData;
  delete pxCtrl;

  // return our CRC-32 value
  //
  return( ulCRC );
}
/* End of function "VMCrc32::CRC32ThreadProc"
/*****************************************************************************/


/*****************************************************************************/
/*

     FUNCTION NAME:  VMCrc32::VMCrc32

       DESCRIPTION:  ctor. sets up the CRC-32 reference table

             INPUT:  void  
            OUTPUT:  none

           RETURNS:  none
*/
VMCrc32::VMCrc32( void )
{
  // This is the official polynomial used by CRC-32 in PKZip, WinZip and Ethernet. 
  //
  ULONG ulPolynomial = 0x04C11DB7;

  // 256 values representing ASCII character codes.
  //
  for (int iOuter = 0; iOuter <= 0xFF; iOuter++ )
  {
    m_aulTable[ iOuter ] = Reflect( iOuter, 8 ) << 24;
    for ( int iInner = 0; iInner < 8; iInner++ )
    {
      m_aulTable[ iOuter ] = (m_aulTable[ iOuter ] << 1) ^ (m_aulTable[ iOuter ] & (1 << 31) 
        ? ulPolynomial : 0 );
    }
    m_aulTable[ iOuter ] = Reflect( m_aulTable[ iOuter ], 32 );
  }
}
/* End of function "VMCrc32::VMCrc32"
/*****************************************************************************/


/*****************************************************************************/
/*

     FUNCTION NAME:  VMCrc32::Reflect

       DESCRIPTION:  used by the constructor to help set up the CRC-32 reference 
                     table

             INPUT:  ulToReflect - the value to be reflected
                     chBitCount - the number of bits to move
            OUTPUT:  

           RETURNS:  the new value
*/
ULONG VMCrc32::Reflect( ULONG ulToReflect, char chBitCount )
{
  ULONG ulValue = 0;

  // Swap bit 0 for bit 7 bit 1 for bit 6, etc.
  //
  for( int iLoop = 1; iLoop < ( chBitCount + 1 ); iLoop++ )
  {
    if ( ulToReflect & 1 )
    {
      ulValue |= 1 << ( chBitCount - iLoop );
    }
    ulToReflect >>= 1;
  }
  return( ulValue );
}
/* End of function "VMCrc32::Reflect"
/*****************************************************************************/


/*****************************************************************************/
/*

     FUNCTION NAME:  VMCrc32::Calculate

       DESCRIPTION:  Calculates the CRC-32 value for the given buffer

             INPUT:  pbBuffer - pointer to the data bytes
                     iBufferSize - the size of the buffer
                     rulOutput - the initial CRC-32 value
            OUTPUT:  value of rulOutput will be modified

           RETURNS:  void  - 
*/
void VMCrc32::Calculate( const LPBYTE pbBuffer, UINT iBufferSize, ULONG& rulOutput )
{
  LPBYTE pByte = pbBuffer;

  while ( iBufferSize-- )
  {
    rulOutput = ( rulOutput >> 8 ) ^ m_aulTable[ (rulOutput & 0xFF ) ^ *pByte++ ];
  }
}
/* End of function "VMCrc32::Calculate"
/*****************************************************************************/


/*****************************************************************************/
/*

     FUNCTION NAME:  VMCrc32::CalcCRC

       DESCRIPTION:  calculates the CRC-32 value for the given buffer
             Note :  ProgressWnd is passed through the IsWindow() API function.
                     If IsWindow() returns zero, CalcCRC() will calculate the 
                     CRC-32 value directly. if IsWindow() returns nonzero, 
                     CalcCRC() will start a seperate thread. The thread will 
                     send PBM_* progress bar messages to the ProgressWnd. When 
                     the thread is finished, the thread will send a message of
                     type WM_CRC_THREAD_DONE to the parent window of the progress
                     window. the WPARAM parameter will contain the HANDLE of 
                     the thread, and the LPARAM will contain the CRC-32 value 
                     of the buffer.

             INPUT:  pvBuffer - a pointer to the data bytes
                     iBufferSize - the size of the buffer
                     hWndProgress - the HWND of the progress bar
            OUTPUT:  

           RETURNS:  if ProgressWnd is not a window returns the CRC-32 value 
                     of the buffer
                     if ProgressWnd is a window, returns the HANDLE of the 
                     created thread
                     returns NULL if an error occurs
*/
DWORD VMCrc32::CalcCRC( LPVOID pvBuffer, UINT iBufferSize, HWND hWndProgress )
{
  // check the validity of the data
  //
  if ( !pvBuffer || !iBufferSize )
  {
    return( 0 ); 
  }

  if ( !IsWindow( hWndProgress ) )
  {
    // calculate CRC directly
    //
    DWORD dwCRC = 0xFFFFFFFF;
    Calculate( (LPBYTE)pvBuffer, iBufferSize, dwCRC );
    return( dwCRC ^ 0xFFFFFFFF );
  }

  // start the thread
  //
  P_VM_CRC_THREAD_STRUCT pxCtrl = new VM_CRC_THREAD_STRUCT;
  DWORD               dwThreadID;
  HANDLE              hThread;
  hThread = ::CreateThread( NULL, 
                            0, 
                            CRC32ThreadProc, 
                            (LPVOID)pxCtrl,
                            CREATE_SUSPENDED, 
                            &dwThreadID );
  if ( hThread )
  {
    // thread successfully created
    //
    pxCtrl->m_poCRC32        = this;
    pxCtrl->m_achFileName[0] = 0;
    pxCtrl->m_hWndProgress   = hWndProgress;
    pxCtrl->m_hThread        = hThread;
    pxCtrl->m_iSize          = iBufferSize;
    pxCtrl->m_pbData         = new BYTE[ iBufferSize ];
    memcpy( pxCtrl->m_pbData, pvBuffer, iBufferSize );
    ::ResumeThread( hThread );
  }
  else
  {
    // thread creation failed, clean up
    //
    delete pxCtrl;
  }
  return( (DWORD)hThread );
}
/* End of function "VMCrc32::CalcCRC"
/*****************************************************************************/


/*****************************************************************************/
/*

     FUNCTION NAME:  VMCrc32::CalcCRC

       DESCRIPTION:  calculates the CRC-32 value for the given buffer
             Note :  ProgressWnd is passed through the IsWindow() API function.
                     If IsWindow() returns zero, CalcCRC() will calculate the 
                     CRC-32 value directly. if IsWindow() returns nonzero, 
                     CalcCRC() will start a seperate thread. The thread will 
                     send PBM_* progress bar messages to the ProgressWnd. When 
                     the thread is finished, the thread will send a message of
                     type WM_CRC_THREAD_DONE to the parent window of the progress
                     window. the WPARAM parameter will contain the HANDLE of 
                     the thread, and the LPARAM will contain the CRC-32 value 
                     of the buffer.

             INPUT:  pchFileName - the complete path to the file
                     hWndProgress - the HWND of the progress bar
            OUTPUT:  

           RETURNS:  if ProgressWnd is not a window returns the CRC-32 value 
                     of the buffer
                     if ProgressWnd is a window, returns the HANDLE of the 
                     created thread
                     returns NULL if an error occurs
*/
DWORD VMCrc32::CalcCRC( LPCTSTR pchFileName, HWND hWndProgress )
{
  // make sure the file exists and is not a directory
  //
  DWORD dwAttribs = ::GetFileAttributes( pchFileName );
  if ( dwAttribs == 0xFFFFFFFF || dwAttribs & FILE_ATTRIBUTE_DIRECTORY )
  {
    return( 0 );
  }

  // setup the CRCStruct
  //
  P_VM_CRC_THREAD_STRUCT pxCtrl = new VM_CRC_THREAD_STRUCT;
  pxCtrl->m_poCRC32      = this;
  pxCtrl->m_pbData       = NULL;
  pxCtrl->m_iSize        = 0;
  pxCtrl->m_hWndProgress = hWndProgress;
  pxCtrl->m_hThread      = NULL;
  _tcsncpy( pxCtrl->m_achFileName, pchFileName, MAX_PATH );

  if ( !IsWindow( hWndProgress ) )
  {
    // calculate CRC directly
    //
    return( CRC32ThreadProc( (LPVOID)pxCtrl ) );
  }

  // start the thread
  //
  DWORD  dwThreadID;
  HANDLE hThread;
  hThread = ::CreateThread( NULL, 
                            0, 
                            CRC32ThreadProc, 
                            (LPVOID)pxCtrl, 
                            CREATE_SUSPENDED, 
                            &dwThreadID );
  if ( hThread )
  {
    // thread successfully created
    //
    pxCtrl->m_hThread = hThread;
    ::ResumeThread( hThread );
  }
  else
  {
     // thread creation failed, clean up
     //
     delete pxCtrl;
  }
  return( (DWORD)hThread );
}
/* End of function "VMCrc32::CalcCRC"
/*****************************************************************************/


    
/*****************************************************************************/
/* Check-in history 
   $WorkFile:   $
    $Archive:   $

 *$Log:  $
*/
/*****************************************************************************/


